package jpcap;

import jpcap.packet.Packet;

import java.io.IOException;

/**
 * 该类用于发送报文。
 */
public class JpcapSender extends JpcapInstance {
    private native String nativeOpenDevice(String device);

    private native void nativeSendPacket(Packet packet);

    private native void nativeCloseDevice();

    private native void nativeOpenRawSocket();

    private native void nativeSendPacketViaRawSocket(Packet packet);

    private native void nativeCloseRawSocket();

    private static final int RAW_SOCKET_ID = 99999;

    private JpcapSender() throws IOException {
        if (reserveID() < 0)
            throw new IOException("Unable to open a device: "
                    + MAX_NUMBER_OF_INSTANCE + " devices are already opened.");
    }

    JpcapSender(int ID) {
        this.ID = ID;
    }

    /**
     * 初始化用于发送数据包的网络接口，并返回该类的实例。
     *
     * @param device 发送报文接口
     * @return 该类的实例 (JpcapSender)
     * @throws IOException 当接口初始化失败时被引发
     */
    public static JpcapSender openDevice(NetworkInterface device) throws IOException {
        JpcapSender sender = new JpcapSender();
        String ret = sender.nativeOpenDevice(device.name);

        if (ret == null)
            return sender;
        else
            throw new IOException(ret);
    }

    /**
     * 打开一个原始IP套接字发送一个包.<BR>
     * 当通过原始套接字发送数据包时，数据包的数据链头被忽略
     * (= automatically generated by OS).<P>
     * Note: 原始套接字的实现和行为在不同的操作系统中可能会有所不同.
     * 而且，一次只能打开一个原始套接字.
     */
    @Deprecated
    public static JpcapSender openRawSocket() throws IOException {
        JpcapSender sender = new JpcapSender();
        sender.nativeOpenRawSocket();
        sender.ID = RAW_SOCKET_ID;

        return sender;
    }

    public void close() {
        if (ID == RAW_SOCKET_ID)
            nativeCloseRawSocket();
        else
            nativeCloseDevice();
        unreserveID();
    }

    /**
     * 发送报文.
     * <p/>
     * If this JpcapSender instance was created by openDevice(), you need to set
     * the Datalink layer's header (e.g., Ethernet header) of the packet. <P>
     * <p/>
     * If this JpcapSender instance was created by openRawSocket(), you can only
     * send IP packets, but you may not need to set the Datalink layer's header
     * of the IP packets you want to send.<BR>
     * Note: the implementation and behavior of a raw socket may vary in different OS.
     * For example, in Windows 2000/XP, you need to manually set the datalink/IP headers
     * of a packet.
     *
     * @param packet Packet to be sent
     */
    public void sendPacket(Packet packet) {
        if (ID == RAW_SOCKET_ID)
            nativeSendPacketViaRawSocket(packet);
        else
            nativeSendPacket(packet);
    }
}
